Practice Problems and Solutions 5 -- Dynamic vs Lexical Scope for Free (Global) variables.

You should do these problems on paper or mentally first, and only THEN check the solution.

 

Scope of non-local variables in Python and Java;

First we review how non-local variable references are resolved in Java.

NOTE: There will be no problems on Midterm 2 about Python or Java, but this is fundamental to understanding scope, and I suggest you go through them if you don't know how scope works.

5.1. Show what the following Java program prints out.

public class Main {

  static int n = 2;

  static int test(int k) {
    int n = 5;
    return n + k;
  }

  public static void main(String[] args) {
    int n = 10;
    System.out.println(n);
    System.out.println(test(n));
  }
}

Show Solution

5.2. Show what the following Java program prints out.

public class Main {

  static int n = 2;

  static int test(int k) {
    // int n = 5;                -- Note this line is commented out
    return n + k;
  }

  public static void main(String[] args) {
    int n = 10;
    System.out.println(n);
    System.out.println(test(n));
  }
}

Show Solution

5.3. Show what the following Java program prints out.

public class Main {

  static int n = 2;

  static int test(int k) {
    // int n = 5;                 Note that this line is commented out
    return n + k;
  }

  public static void main(String[] args) {
    // int n = 10;                Note that this line is commented out 
    System.out.println(n);
    System.out.println(test(n));
  }
}

Show Solution

5.4. Show what the following Java program prints out.

public class Main {

  static int n = 0;

  static int test(int k) {
    n = n + 1;                               
    return n + k;
  }

  public static void main(String[] args) {
    int n = 10;                              
    System.out.println(test(n));
    n = 100;
    System.out.println(test(n));
	n = 1000;
    System.out.println(test(n));
  }
}

Show Solution

5.5. Show what the following Java program prints out.

public class Main {

  static int n = 0;

  static int test(int k) {
    n = n + 1;                               
    return n + k;
  }

  public static void main(String[] args) {
    n = 10;                              
    System.out.println(test(n));
    int n = 100;
    System.out.println(test(n));
    n = 1000;
    System.out.println(test(n));
  }
}

Show Solution

Now a similar exercise in Python...

5.6. Show what the following Python program prints out.

n = 2

def test(k):
   n = 5
   return n + k

def main():
   n = 10
   print(n)
   print(test(n))

main()

Show Solution

5.7. What happens with the following Python program and why? (It attempts to simulate the Java program in 5.4.)

n = 0

def test(k):
   n = n + k
   return n + k

def main():
   n = 10
   print(test(n))
   n = 100
   print(test(n))
   n = 1000
   print(test(n))
main()

Show Solution

5.8. How do we fix the problem in the previous problem so that it behaves as the Java program in 5.4?

Show Solution

For Midterm Two, I want you to know how dynamic vs lexical/static scoping works in Haskell, that is, what actually happens in Haskell, and what would happen if Haskell used dynamic scoping. There will be no questions about Python or Java, just Haskell examples, and just about what happens when you define functions and use those definitions. There will be nothing about the REPL's way of handling definitions, and the examples below use the REPL just for convenience; they might equally have been written in a file. The following exercises which show Haskell code are intended to illustrate the kinds of problems you might see on the exam. The Python questions are just for comparison (since I assume you know Python).

Non-Local Variables in Function Definitions in Python compared with Haskell

In problems 5.9 - 5.20, we show that with regard to the definitions made in the top level read-eval-print-loop, Python uses dynamic scoping and Haskell uses lexical scoping.

5.9. Show what the following Python code prints out.

n = 5

def test(k):
    return n + k

print(test(10))
    
n = 10

print(test(10))

Show Solution

5.10. Show what the following Haskell code prints out.

Prelude> n = 5

Prelude> test k = n + k

Prelude> test 10
15

Prelude> n = 10

Prelude> test 10
??

Show Solution

Note: There is nothing special about using the REPL to do these kinds of examples, and the last example could just have easily been written in a file as:

ex5_10a = let    n = 5              in
          let    test k = n + k     in
                 test 10

ex5_10b = let    n = 5              in
          let    test k = n + k     in
          let    n = 10             in
                test 10

5.10b. What's the point of these last two problems?

Show Solution

5.11. Show what the following Python code prints out.

n = 0

def test(k):
    n = 10
    def localTest(i):
        return n + k + i
    
    return localTest(10*k)

print(test(10))
    
n = 1000

print(test(10))

Show Solution

5.12. Show what the following Python code prints out.

n = 0

def test(k):
    #  n = 10                // this line commented out
    def localTest(i):
        return n + k + i
    
    return localTest(10*k)

print(test(10))
    
n = 1000

print(test(10))

Show Solution

5.13. Show what the following Haskell code prints out.

Prelude> :set +m
Prelude> n = 0
Prelude> test k = let n = 10
Prelude|              localTest i = n + k + i
Prelude|          in (localTest 10)*k
Prelude> test 10
300
Prelude> n = 1000
Prelude> test 10
???

Show Solution

5.14. Show what the following Haskell code prints out.

Prelude> n = 0
Prelude> test k = let localTest i = n + k + i
Prelude|          in (localTest 10)*k
Prelude> test 10
200
Prelude> n = 1000
Prelude> test 10
???

Show Solution

5.15. Show what the following Haskell code prints out.

Prelude> n = 1
Prelude> g i = n + i
Prelude> f func k = func (10*k)
Prelude> f g 10
101
Prelude> n = 1000
Prelude> f g 10
???

Show Solution

5.16. Show what the following Haskell code prints out.

Prelude> n = 1
Prelude> g i = n + i
Prelude> f func k = func (10*k)
Prelude> f g 10
101
Prelude> n = 1000
Prelude> f g 10
101
Prelude> f' func k = let n = 10 in func(10*k)
Prelude> f' g 10
???

Show Solution

5.17. Show what the following Haskell code prints out.

Prelude> n = 1
Prelude> f = \x -> let k = n in x * k
Prelude> f 10
10
Prelude> n = 10
Prelude> f 10
??

Show Solution

Now let's try the same thing in Python....

5.18. Show what the following Python code prints out. (= 5.15)

n = 1

def g(i):
    return n + i

def f(func,k):
    return func (10*k)

print(f(g,10))

n = 1000

print(f(g,10))

Show Solution

5.19. Show what the following Python code prints out. (= 5.16)

n = 1

def g(i):
    return n + i

def f(func,k):
    return func (10*k)

print(f(g,10))

n = 1000

print(f(g,10))

def fPrime(func,k):
    return func(10*k)

print(fPrime(g,10))

Show Solution

5.20. Show what the following Python code prints out. (= 5.17)

n = 1

def f(x):
    k = n
    return x * k

print(f(10))
n = 10
print(f(10))

Show Solution

In the remaining problems, we show that with regard to the definitions made locally (not in the REPL), both Python and Haskell use lexical scoping.

5.21. Show what the following Haskell code prints out and explain where the binding for n in the function h comes from.

Prelude> f func x = let n = 1 in func (x + n)

Prelude> g x = let n = 10 
Prelude|       in let h y = n + y 
Prelude|          in f h x

Prelude> g 0
??

Show Solution

5.22. What would be the value produced in the last problem if Haskell used dynamic scoping? Explain carefully.

Show Solution

5.23. What is the value printed by the following Python program and what does it say about the scope rules used for local variables (not defined in REPL) in Python?

def test (func,x):
    n = 2
    return func(x+n,x-n)

def tryIt(x,n):
    def why(x,y):
        return n * x + y
    return test(why,x)

print(tryIt(1,10))

Show Solution

5.24. What would be the value produced if Python used dynamic scoping in the last example? Where does the "free variable capture" take place?

Show Solution

5.25. Show what the following Haskell code prints out and explain where the binding for n in the function h comes from.

-- In the file test.hs

f :: (Integer -> Integer) -> Integer -> Integer
f g x = let n = 1 
        in g ((\n -> g (x + n)) 100)

test :: Integer -> Integer
test x = f g x
    where n = 3
          g = \x -> n + x
		  
		
Main> test 1000
????

Show Solution

5.26. What would be the value produced in the last problem if Haskell used dynamic scoping? Explain carefully.

Show Solution

5.27. What is the value printed by the following Python program and what does it say about the scope rules used for local variables (not defined in REPL) in Python?

def f (func,x):
    n = 1
    return func(x+n)

def g(x):
    n = 10
    def h(y):
        return n + y
    return f(h,x)

print(g(0))

Show Solution

5.28. What would be the value produced if Python used dynamic scoping to find the binding for the non-local variable n when h was evaluated inside the function f?

Show Solution

5.29. Does the following code cause an error in Python (i.e., before you call the function f)?

def g(func,x):
    n = 1
    return func(x+n)

def f(x):
    return n + x

Show Solution

5.30. Does the following code cause an error in Python (i.e., AFTER you call the function f)?

def g(func,x):
    n = 1
    return func(x+n)

def f(x):
    return n + x
	
print(g(f,0))

Show Solution

5.31. Does the following code cause an error in Haskell (i.e., before you call the function f)?

def g(func,x):
    n = 1
    return func(x+n)

def f(x):
    return n + x

Show Solution

5.32. What accounts for the differing behavior in Haskell and Python as shown in the last three problems?

Show Solution